package service

import (
	"SecKill/common/helper"
	"SecKill/common/lock"
	"SecKill/common/models"
	"SecKill/common/response"
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
	"os"
	"strconv"
	"sync"
	"time"
)

var userLocks sync.Map

func tryUserLock(userId int) *sync.Mutex {
	lock, _ := userLocks.LoadOrStore(userId, &sync.Mutex{})
	return lock.(*sync.Mutex)
}

var userLocksChannel sync.Map

func getUserLockByChannel(userId int) chan struct{} {
	lock, _ := userLocksChannel.LoadOrStore(userId, make(chan struct{}, 1))
	return lock.(chan struct{})
}
func lockUserLockByChannel(userId int) {
	lock := getUserLockByChannel(userId)
	lock <- struct{}{}
}
func unlockUserLockByChannel(userId int) {
	lock := getUserLockByChannel(userId)
	<-lock
}
func init() {
	//channel阻塞队列
	//go StartVoucherOrderQueueListener()
	//redis消息队列
	go StartRedisMessageQueue()
}

var voucherOrderQueue = make(chan models.VoucherOrder, 100)

func AddVoucherOrderToQueue(voucherOrder models.VoucherOrder) {
	voucherOrderQueue <- voucherOrder
}

func StartVoucherOrderQueueListener() {
	//监听订单信道
	for voucherOrder := range voucherOrderQueue {
		fmt.Println("监听到订单信息：", voucherOrder)
		userId := voucherOrder.UserId
		voucherId := voucherOrder.VoucherId
		tx := models.Db.Begin()
		lock := tryUserLock(userId)
		for !lock.TryLock() {
			time.Sleep(100 * time.Millisecond)
		}
		defer lock.Unlock()
		var count int64
		models.Db.Model(new(models.VoucherOrder)).Where("user_id = ?", userId).Where("voucher_id = ?", voucherId).Count(&count)
		if count > 0 {
			continue
		}
		if err := tx.Model(new(models.SeckillVoucher)).
			Where("voucher_id = ?", voucherOrder.VoucherId).
			Where("stock > ?", 0).
			Update("stock", gorm.Expr("stock - ?", 1)).
			Error; err != nil {
			fmt.Println(err)
			tx.Rollback()
			continue
		}

		if err := tx.Model(new(models.VoucherOrder)).Create(&voucherOrder).Error; err != nil {
			fmt.Println(err)
			tx.Rollback()
			continue
		}
		if err := tx.Commit().Error; err != nil {
			fmt.Println(err)
			tx.Rollback()
			continue
		}

	}
}
func StartRedisMessageQueue() {
	key := "stream.orders"
	group := "group1"
	args := redis.XReadGroupArgs{
		Group:    group,
		Consumer: "consumer1",
		Streams:  []string{key, ">"},
		Count:    1,
		Block:    2 * time.Second,
		NoAck:    false,
	}
	for {
		result, err := models.RDb.XReadGroup(context.Background(), &args).Result()
		if err != nil {
			continue
		}
		for _, stream := range result {
			for _, message := range stream.Messages {
				// 获取消息内容
				userId, _ := strconv.Atoi(message.Values["userId"].(string))
				voucherId, _ := strconv.Atoi(message.Values["voucherId"].(string))
				orderId, _ := strconv.Atoi(message.Values["id"].(string))

				// 处理消息

				var voucherOrder models.VoucherOrder
				voucherOrder.Id = orderId
				voucherOrder.UserId = userId
				voucherOrder.VoucherId = voucherId
				err = models.Db.Model(new(models.VoucherOrder)).Create(&voucherOrder).Error
				if err != nil {
					go OrderPendingListListener()
					continue
				}

				models.RDb.XAck(context.Background(), key, group, message.ID)
			}
		}

	}
}
func OrderPendingListListener() {
	key := "stream.orders"
	group := "group1"
	argsOfPendingList := redis.XReadGroupArgs{
		Group:    group,
		Consumer: "consumer1",
		Streams:  []string{key, "0"},
		Count:    1,
		Block:    2 * time.Second,
		NoAck:    false,
	}
	for {
		resultP, errP := models.RDb.XReadGroup(context.Background(), &argsOfPendingList).Result()
		if errP != nil {
			return
		}
		for _, streamP := range resultP {
			for _, messageP := range streamP.Messages {
				// 获取消息内容
				userIdP := messageP.Values["userId"]
				voucherIdP := messageP.Values["voucherId"]
				orderIdP := messageP.Values["id"]

				// 处理消息
				var voucherOrderP models.VoucherOrder
				voucherOrderP.Id = orderIdP.(int)
				voucherOrderP.UserId = userIdP.(int)
				voucherOrderP.VoucherId = voucherIdP.(int)
				models.Db.Model(new(models.VoucherOrder)).Create(&voucherOrderP)
			}
		}
	}
}
func AddVoucherOrderByLuaV2(c *gin.Context) {
	userId, _ := c.Get("userId")
	voucherId, _ := strconv.Atoi(c.Param("id"))
	var seckillVoucher models.SeckillVoucher
	err := models.Db.Model(new(models.SeckillVoucher)).Where("voucher_id = ?", voucherId).Find(&seckillVoucher).Error
	if err == gorm.ErrRecordNotFound {
		response.ResponseFail(c)
		return
	}

	if time.Time(seckillVoucher.BeginTime).After(time.Now()) {
		response.ResponseFailWithData(c, "秒杀尚未开始！", nil, 0)
		return
	}
	if time.Time(seckillVoucher.EndTime).Before(time.Now()) {
		response.ResponseFailWithData(c, "秒杀已结束！", nil, 0)
		return
	}
	seckillScirpt, err := os.ReadFile("service/seckill_v2.lua")
	if err != nil {
		fmt.Println(err)
		response.ResponseFail(c)
		return
	}
	orderId := helper.NextId("order")
	result := models.RDb.Eval(context.Background(), string(seckillScirpt), []string{}, voucherId, userId, orderId).Val()
	if result != int64(0) {
		if result == int64(1) {
			response.ResponseFailWithData(c, "库存不足！", nil, 0)
			return
		} else {
			response.ResponseFailWithData(c, "不能重复下单！", nil, 0)
			return
		}
	}

	response.ResponseOKWithData(c, "下单成功", orderId, 1)
	return

}
func AddVoucherOrderByLua(c *gin.Context) {
	userId, _ := c.Get("userId")
	voucherId, _ := strconv.Atoi(c.Param("id"))
	var seckillVoucher models.SeckillVoucher
	err := models.Db.Model(new(models.SeckillVoucher)).Where("voucher_id = ?", voucherId).Find(&seckillVoucher).Error
	if err == gorm.ErrRecordNotFound {
		response.ResponseFail(c)
		return
	}

	if time.Time(seckillVoucher.BeginTime).After(time.Now()) {
		response.ResponseFailWithData(c, "秒杀尚未开始！", nil, 0)
		return
	}
	if time.Time(seckillVoucher.EndTime).Before(time.Now()) {
		response.ResponseFailWithData(c, "秒杀已结束！", nil, 0)
		return
	}
	seckillScirpt, err := os.ReadFile("service/seckill.lua")
	if err != nil {
		fmt.Println(err)
		response.ResponseFail(c)
		return
	}
	result := models.RDb.Eval(context.Background(), string(seckillScirpt), []string{}, voucherId, userId).Val()
	if result != int64(0) {
		if result == int64(1) {
			response.ResponseFailWithData(c, "库存不足！", nil, 0)
			return
		} else {
			response.ResponseFailWithData(c, "不能重复下单！", nil, 0)
			return
		}
	}

	orderId := helper.NextId("order")
	response.ResponseOKWithData(c, "下单成功", orderId, 1)
	var voucherOrder models.VoucherOrder
	voucherOrder.Id = int(orderId)
	voucherOrder.UserId = userId.(int)
	voucherOrder.VoucherId = voucherId
	//存入阻塞队列
	AddVoucherOrderToQueue(voucherOrder)

}
func AddVoucherOrder(c *gin.Context) {
	voucherId, _ := strconv.Atoi(c.Param("id"))
	var seckillVoucher models.SeckillVoucher
	tx := models.Db.Begin()
	err := tx.Model(new(models.SeckillVoucher)).Where("voucher_id = ?", voucherId).Find(&seckillVoucher).Error
	if err == gorm.ErrRecordNotFound {
		response.ResponseFail(c)
		return
	}

	if time.Time(seckillVoucher.BeginTime).After(time.Now()) {
		response.ResponseFailWithData(c, "秒杀尚未开始！", nil, 0)
		return
	}
	if time.Time(seckillVoucher.EndTime).Before(time.Now()) {
		response.ResponseFailWithData(c, "秒杀已结束！", nil, 0)
		return
	}

	if seckillVoucher.Stock < 1 {
		response.ResponseFailWithData(c, "库存不足！", nil, 0)
		return
	}
	userId, _ := c.Get("userId")
	createVoucherOrder(c, userId.(int), voucherId, tx)
	return

}

func createVoucherOrder(c *gin.Context, userId int, voucherId int, tx *gorm.DB) {
	//userLock := tryUserLock(userId)
	//userLock.Lock()
	//defer userLock.Unlock()
	userLock := lock.RedisLock{
		Key: "order:" + strconv.Itoa(userId)}
	if !userLock.TryLock(time.Second * 5) {
		response.ResponseFailWithData(c, "系统繁忙，请稍后重试！", nil, 0)
		return
	}
	defer userLock.UnLock()
	var count int64
	models.Db.Model(new(models.VoucherOrder)).Where("user_id = ?", userId).Where("voucher_id = ?", voucherId).Count(&count)
	if count > 0 {
		response.ResponseFailWithData(c, "您已购买过该优惠券！", nil, 0)
		return
	}

	if err := tx.Model(new(models.SeckillVoucher)).
		Where("voucher_id = ?", voucherId).
		Where("stock > ?", 0).
		Update("stock", gorm.Expr("stock - ?", 1)).
		Error; err != nil {
		tx.Rollback()
		response.ResponseFailWithData(c, "库存不足！", nil, 0)
		return
	}

	var voucherOrder models.VoucherOrder
	voucherOrder.Id = int(helper.NextId("order"))
	voucherOrder.UserId = userId
	voucherOrder.VoucherId = voucherId
	if err := tx.Model(new(models.VoucherOrder)).Create(&voucherOrder).Error; err != nil {
		tx.Rollback()
		response.ResponseFail(c)
		return
	}
	if err := tx.Commit().Error; err != nil {
		tx.Rollback()
		response.ResponseFail(c)
		return
	}
	response.ResponseOKWithData(c, "下单成功", voucherOrder.Id, 1)
}
